home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Enigma Amiga Life 109
/
EnigmaAmiga109CD.iso
/
dalla rivista
/
amiga.free
/
sorgenti vari
/
wolfedit2 2.0.4 source.sit
/
WolfEdit2 2.0.4 Source
/
ULZSS.p
< prev
next >
Wrap
Text File
|
1996-04-27
|
13KB
|
527 lines
unit ULZSS;
interface
type
BytesPtr = ^BytesArray;
BytesArray = packed array[0..999999] of 0..255;
function LZSS (src: univ BytesPtr; srcLen: longint): Handle;
function LZSSX (skip: longint; header: univ BytesPtr; headerLen: longint; data: univ BytesPtr; dataLen: longint; compressing: boolean): Handle;
procedure DLZSS (src, dst: univ BytesPtr; dstLen: longint);
procedure DLZSSX (src, dst: univ BytesPtr; skip, dstLen: longint);
procedure TransposePixels (src, dst: univ longint; width, height: integer);
procedure UnpackObject (src: univ longint; srcSize: longint; dst: univ longint; dstWidth: integer);
function PackObject (src: univ BytesPtr; width, height: integer): Handle;
implementation
uses
UGoof;
const
compressingAlertDlogID = 134;
compressingAlertStringsID = 130;
outOfMemAlrtID = 148;
progressItem = 2;
type
PackedByte = packed array[0..0] of 0..255;
BytePtr = ^PackedByte;
WordPtr = ^integer;
var
gAlert: DialogPtr;
gProgressRect: Rect;
gProgress, gMaxProgress: integer;
gOldPort: GrafPtr;
{$D+}
procedure ShowWatch;
begin
gCurrentCursor := GetCursor(watchCursor);
SetCursor(gCurrentCursor^^);
end;
{$D+}
procedure ShowCompressingAlert (compressing: boolean);
var
iType: integer;
iHandle: Handle;
s: Str255;
begin
GetPort(gOldPort);
GetIndString(s, compressingAlertStringsID, ord(compressing) + 1);
ParamText(s, '', '', '');
gAlert := GetNewDialog(compressingAlertDlogID, nil, WindowPtr(-1));
GetDItem(gAlert, progressItem, iType, iHandle, gProgressRect);
gProgress := 0;
gMaxProgress := gProgressRect.right - gProgressRect.left;
ShowWindow(gAlert);
DrawDialog(gAlert);
SetPort(gAlert);
FrameRect(gProgressRect);
ShowWatch;
end;
{$D+}
procedure SetProgress (numer, denom: longint);
var
r: Rect;
begin
gProgress := round((numer / denom) * gMaxProgress);
r := gProgressRect;
r.right := r.left + gProgress;
FillRect(r, black);
end;
{$D+}
procedure HideCompressingAlert;
begin
DisposeDialog(gAlert);
SetPort(gOldPort);
end;
{$D-}
procedure TransposePixels (src, dst: univ longint; width, height: integer);
var
p, q: longint;
row, col: integer;
begin
p := src;
for row := 0 to height - 1 do begin
q := dst + row;
for col := 0 to width - 1 do begin
BytePtr(q)^[0] := BytePtr(p)^[0];
p := p + 1;
q := q + height;
end;
end;
end;
{$D+}
function LZSS (src: univ BytesPtr; srcLen: longint): Handle;
begin
LZSS := LZSSX(0, nil, 0, src, srcLen, true);
end;
{$D+}
procedure FindMatchScrewUp;
begin
Panic('LZSSX: FindMatch', 'MatchLength and PMatchLength gave different results');
end;
{$D-}
function LZSSX (skip: longint; header: univ BytesPtr; headerLen: longint; data: univ BytesPtr; dataLen: longint; compressing: boolean): Handle;
const
maxMatch = 18; {Maximum match length we can encode}
var
src: BytesPtr; {source data}
srcLen: longint; {length of source data}
h: Handle; {place to put compressed data}
dst: BytesPtr; {h^}
sp, dp: longint; {src and dst pointers}
flagPos: longint; {offset of current flags byte}
flagCount: integer; {number of flags in current flags byte}
index: array[0..255] of longint; {byte -> offset of last matching byte}
chain: array[0..$FFF] of longint; {offset -> offset of prev matching byte}
prog, progStep: longint;
{Advance source pointer and update chains}
procedure Advance (n: longint);
var
byte: integer;
begin
prog := prog - n;
if prog <= 0 then begin
SetProgress(sp, srcLen);
prog := progStep;
end;
if compressing then
while n > 0 do begin
byte := src^[sp];
chain[BAND(sp, $FFF)] := index[byte];
index[byte] := sp;
sp := sp + 1;
n := n - 1;
end
else
sp := sp + n;
end;
{Store a byte of compressed data}
procedure PutByte (x: integer);
begin
dst^[dp] := x;
dp := dp + 1;
end;
{Store a word of compressed data, little-endian}
procedure PutWord (x: integer);
begin
dst^[dp] := BAND(x, $FF);
dst^[dp + 1] := BSR(x, 8);
dp := dp + 2;
end;
{Store a flag, making a new flags byte if necessary}
procedure PutFlag (f: integer);
begin
if flagCount = 8 then begin
flagPos := dp;
PutByte(0);
flagCount := 0;
end;
dst^[flagPos] := dst^[flagPos] + BSL(f, flagCount);
flagCount := flagCount + 1;
end;
{Find the number of bytes matching at pos1 and pos2}
function MatchLength (pos1, pos2: longint): integer;
var
pos0, maxPos: longint;
begin
pos0 := pos1;
maxPos := pos0 + maxMatch;
if maxPos > srcLen then
maxPos := srcLen;
while (pos1 < maxPos) & (src^[pos1] = src^[pos2]) do begin
pos1 := pos1 + 1;
pos2 := pos2 + 1;
end;
MatchLength := pos1 - pos0;
end;
{$IFC FALSE}
function MatchLength (addr1, maxAddr1, addr2: univ Ptr): integer;
inline
$245F,{00000000: 245F MOVEA.L (A7)+,A2}
$205F,{00000002: 205F MOVEA.L (A7)+,A0}
$225F,{00000004: 225F MOVEA.L (A7)+,A1}
$2009, {00000006: 2009 MOVE.L A1,D0}
$6004,{00000008: 6004 BRA.S *+$0006 ; 0000000E}
$B509,{0000000A: B509 CMPM.B (A1)+,(A2)+}
$6606,{0000000C: 6606 BNE.S *+$0008 ; 00000014}
$B1C9,{0000000E: B1C9 CMPA.L A1,A0}
$66F8,{00000010: 66F8 BNE.S *-$0006 ; 0000000A}
$6002,{00000012: 6002 BRA.S *+$0004 ; 00000016}
$5389,{00000014: 5389 SUBQ.L #$1,A1}
$93C0,{00000016: 93C0 SUBA.L D0,A1}
$3E89;{00000018: 3E89 MOVE.W A1,(A7)}
{$ENDC}
{Search back through the chains for the longest match}
{Return true if it is at least 3 bytes long}
function FindMatch (var bestPos, bestLen: longint): boolean;
var
pos, len: longint; {Offset and length of match being considered}
{max: longint;}
window: longint; {Earliest offset at which a match is valid}
begin
window := sp - $1000;
if window < 0 then
window := 0;
bestLen := 0;
pos := index[src^[sp]];
while (pos >= window) & (bestLen < maxMatch) do begin
len := MatchLength(pos, sp);
{$IFC FALSE}
max := pos + maxMatch;
if max > srcLen then
max := srcLen;
len := MatchLength(@src^[pos], @src^[max], @src^[sp]);
if (len <> plen) then
FindMatchScrewUp;
{$ENDC}
if len > bestLen then begin
bestPos := pos;
bestLen := len;
end;
pos := chain[BAND(pos, $FFF)];
end;
FindMatch := bestLen >= 3;
end;
procedure InitChains;
var
i: integer;
begin
for i := 0 to 255 do
index[i] := -1;
for i := 0 to $FFF do
chain[i] := -1;
end;
{Main loop}
procedure Compress (block: BytesPtr; blockLen: longint);
var
pos, len: longint; {position and length of match}
begin
InitChains;
src := block;
sp := 0;
srcLen := blockLen;
while sp < srcLen do begin
if compressing & FindMatch(pos, len) then begin
PutFlag(0);
PutWord(BOR(BSL(len - 3, 12), $1000 - (sp - pos)));
Advance(len);
end
else begin
PutFlag(1);
PutByte(src^[sp]);
Advance(1);
end;
end;
end;
{$D+}
begin {LZSSX}
h := NewHandle(skip + headerLen + dataLen + (headerLen + dataLen + 7) div 8);
if h = nil then
DoAlert(outOfMemAlrtID)
else begin
ShowCompressingAlert(compressing);
progStep := dataLen div 200;
prog := progStep;
HLock(h);
dst := BytesPtr(h^);
dp := skip;
flagCount := 8;
if headerLen > 0 then
Compress(header, headerLen);
Compress(data, dataLen);
SetHandleSize(h, dp);
{writeln('Compressed ', srcLen : 1, ' to ', dp : 1, ' bytes (', 100.0 * dp / srcLen : 1 : 1, '%)');}
HUnlock(h);
HideCompressingAlert;
end;
LZSSX := h;
end;
{$D-}
procedure DLZSS (src, dst: univ BytesPtr; dstLen: longint);
var
sp, dp: longint;
flagCount: integer;
flags: integer;
item: integer;
copyEnd: longint;
pos: longint;
begin {DLZSS}
sp := 0;
dp := 0;
flagCount := 0;
while dp < dstLen do begin
if flagCount = 0 then begin
flags := src^[sp];
sp := sp + 1;
flagCount := 8;
end;
if odd(flags) then begin
dst^[dp] := src^[sp];
sp := sp + 1;
dp := dp + 1;
end
else begin
item := src^[sp] + BSL(src^[sp + 1], 8);
sp := sp + 2;
pos := dp - $1000 + BAND(item, $FFF);
copyEnd := dp + 3 + BAND($F, BSR(item, 12));
if copyEnd > dstLen then
copyEnd := dstLen;
while dp < copyEnd do begin
dst^[dp] := dst^[pos];
dp := dp + 1;
pos := pos + 1;
end;
end;
flags := BSR(flags, 1);
flagCount := flagCount - 1;
end;
end;
{$D-}
procedure DLZSSX (src, dst: univ BytesPtr; skip, dstLen: longint);
var
sp, dp: longint;
flagCount: integer;
flags: integer;
item: integer;
copyEnd: longint;
pos: longint;
begin {DLZSS}
sp := 0;
dp := 0;
flagCount := 0;
while dp < dstLen do begin
if flagCount = 0 then begin
flags := src^[sp];
sp := sp + 1;
flagCount := 8;
end;
if odd(flags) then begin
if skip > 0 then
skip := skip - 1
else begin
dst^[dp] := src^[sp];
dp := dp + 1;
end;
sp := sp + 1;
end
else begin
item := src^[sp] + BSL(src^[sp + 1], 8);
sp := sp + 2;
pos := dp - $1000 + BAND(item, $FFF);
copyEnd := dp + 3 + BAND($F, BSR(item, 12));
if copyEnd > dstLen then
copyEnd := dstLen;
while dp < copyEnd do begin
if skip > 0 then
skip := skip - 1
else begin
dst^[dp] := dst^[pos];
dp := dp + 1;
end;
pos := pos + 1;
end;
end;
flags := BSR(flags, 1);
flagCount := flagCount - 1;
end;
end;
{$D+}
{$D-}
procedure UnpackObject (src: univ longint; srcSize: longint; dst: univ longint; dstWidth: integer);
var
numSpans: integer;
spanPtr, srcPtr, dstPtr: longint;
offset: integer;
x, y, y0, y1: integer;
begin
numSpans := WordPtr(src)^;
spanPtr := src + 2 + 2 * numSpans;
x := 0;
dst := dst + (dstWidth - numSpans) div 2;
while numSpans > 0 do begin
while WordPtr(spanPtr)^ <> $FFFF do begin
y0 := WordPtr(spanPtr)^ div 2;
y1 := WordPtr(spanPtr + 2)^ div 2;
offset := WordPtr(spanPtr + 4)^;
spanPtr := spanPtr + 6;
srcPtr := src + y0 + offset; {This is really screwy!}
dstPtr := dst + x + y0 * dstWidth;
for y := y0 to y1 - 1 do begin
BytePtr(dstPtr)^[0] := BytePtr(srcPtr)^[0];
srcPtr := srcPtr + 1;
dstPtr := dstPtr + dstWidth;
end;
end;
spanPtr := spanPtr + 2;
x := x + 1;
numSpans := numSpans - 1;
end;
end;
{$D-}
function PackObject (src: univ BytesPtr; width, height: integer): Handle;
type
WordsHandle = ^WordsPtr;
WordsPtr = ^WordsArray;
WordsArray = array[0..32767] of integer;
var
spans: WordsHandle;
s, s0, s1, t, i, p, q: longint;
x, x0, x1, y, y0, y1, offset: integer;
spanbase, pixbase, pixels: longint;
buffer: Handle;
bufwords: WordsPtr;
bufbytes: BytesPtr;
begin
spans := WordsHandle(NewHandle(sizeof(WordsArray)));
s := 0;
pixels := 0;
for x := 0 to width - 1 do begin
p := x;
y := 0;
while (y < height) do begin
while (y < height) & (src^[p] = 0) do begin
y := y + 1;
p := p + width;
end;
y0 := y;
offset := p;
while (y < height) & (src^[p] <> 0) do begin
y := y + 1;
p := p + width;
end;
y1 := y;
if (y0 < y1) then begin
spans^^[s] := 2 * y0;
spans^^[s + 1] := 2 * y1;
spans^^[s + 2] := offset;
s := s + 3;
pixels := pixels + (y1 - y0);
end;
end;
spans^^[s] := -1;
s := s + 1;
end;
x0 := 0;
x1 := width;
s0 := 0;
s1 := s;
while (s0 < s1) & (spans^^[s0] = -1) & (s1 > 0) & ((s1 < 2) | (spans^^[s1 - 2] = -1)) do begin
s0 := s0 + 1;
x0 := x0 + 1;
s1 := s1 - 1;
x1 := x1 - 1;
end;
spanbase := 1 + (x1 - x0);
pixbase := 2 * (spanbase + (s1 - s0));
buffer := NewHandle(pixbase + pixels);
bufwords := WordsPtr(buffer^);
bufbytes := BytesPtr(buffer^);
bufwords^[0] := x1 - x0;
i := 1;
s := s0;
t := spanbase;
q := pixbase;
while s < s1 do begin
bufwords^[i] := 2 * t;
i := i + 1;
while spans^^[s] <> -1 do begin
y0 := spans^^[s] div 2;
y1 := spans^^[s + 1] div 2;
p := spans^^[s + 2];
s := s + 3;
bufwords^[t] := 2 * y0;
bufwords^[t + 1] := 2 * y1;
bufwords^[t + 2] := q - y0;
t := t + 3;
for y := y0 to y1 - 1 do begin
bufbytes^[q] := src^[p];
p := p + width;
q := q + 1;
end;
end;
bufwords^[t] := -1;
s := s + 1;
t := t + 1;
end;
DisposHandle(Handle(spans));
PackObject := Handle(buffer);
end;
end.